package 基础;


/*一、引言
在日常的开发中，我们可能经常能看到如下的代码：

PrivateKeyDecryptParam param = new PrivateKeyDecryptParam.Builder()
                                              .uAppId(uAppId)
                                              .containerId(containerId)
                                              .cipher(cipher)
                                              .signFlag(signFlag)
                                              .build();

在Android中，也会看到下面创建AlertDialog代码：

new AlertDialog.Builder(this)
           .setTitle("标题")
           .setMessage("内容")
           .setCancelable(true)
           .setOnCancelListener(new DialogInterface.OnCancelListener() {
               @Override
               public void onCancel(DialogInterface dialog) {
                
               }
           })
           .show();

观察上面这两段代码，都有一个共同的特点，就是都可以进行链式操作，这就是我们要学习的Builder模式，下面就来详细学习一下。

二、Builder模式的使用场景
在《Effective Java第2版》书中有提到，当遇到多个构造器参数时，要考虑使用构建器（Builder模式）。

举个例子，比如在项目中，我们需要新建一个Person类，假设该类有7个属性（现实中远不止这几个参数），其中有2个是必要的参数需要初始化，分别是id和name。

1. 使用JavaBean的setter方法来设置对象属性
最常见的写法应该是写成JavaBean。代码如下：

public class Person {
    //必要参数
    private int id;
    private String name;
    //可选参数
    private int age;
    private String sex;
    private String phone;
    private String address;
    private String desc;

  // set/get方法忽略
}

在使用该Person类的时候，会写出如下的代码：

Person person = new Person();
person.setId(1);
person.setName("张小毛");
person.setAge(22);
person.setSex("男");
person.setPhone("19999999999");
person.setAddress("beijing");
person.setDesc("JavaBeans模式");

这种JavaBean方式也是常见的构造对象并赋值的方式，这种方式的好处是：易于阅读，并且可以只对有用的成员变量赋值
但它的缺点是：成员变量不可以是 final 类型，失去了不可变对象的很多好处
对象状态不连续，你必须调用7次setter方法才能得到一个具备7个属性值得变量，在这期间用户可能拿到不完整状态的对象。如果有N个属性，岂不是要person.setXXX调
用N次？此种方式不优雅,最重要的缺点是第二条：对象状态不连续。 什么意思呢？

解释一下：这种方式是先创建对象、后赋值，用户不知道什么时候拿到的对象是完整的，构建完成的。很有可能你只setter了一两个属性就返回了，一些必要的属性没有被赋值。

2. 使用重叠构造器
在这种模式下，需要提供一个只有必要参数的构造器，第二个构造器有一个可选参数，第三个有两个可选参数，依此类推，最后一个构造器包含所有的可选参数。代码如下：

public class Person {
    //必要参数
    private final int id;
    private final String name;
    //可选参数
    private int age;
    private String sex;
    private String phone;
    private String address;
    private String desc;

    public Person(int id, String name) {
        this(id, name, 0);
    }

    public Person(int id, String name, int age) {
        this(id, name, age, "");
    }

    public Person(int id, String name, int age, String sex) {
        this(id, name, age, sex, "");
    }

    public Person(int id, String name, int age, String sex, String phone) {
        this(id, name, age, sex, phone, "");
    }

    public Person(int id, String name, int age, String sex, String phone,String address) {
        this(id, name, age, sex, phone, address, "");
    }

    public Person(int id, String name, int age, String sex, String phone,String address, String desc) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.phone = phone;
        this.address = address;
        this.desc = desc;
    }
}


从上面的代码可以看出，当你想要创建实例的时候，就利用参数列表最短的构造器，但该列表中包含了要设置的所有参数，其余都为默认值：

Person person = new Persion(1, "张小毛");

这个构造器调用，通常需要许多你本不想设置的参数，但还是不得不为它们传递值。

这种方式的优点就是：简单！！！！（这是对开发者而言），但使用者在使用时，可得仔细了解你每个构造函数，否则一不小心填错顺序也不知道。而且如果有十几个属性，
就歇菜了……（我也没见过有十几个参数的构造函数）

所以缺点是：只适用于成员变量少的情况，太多了不容易理解、维护。

简而言之：重叠构造器可行，但是当有许多参数的时候，创建使用代码会很难写，并且较难以阅读。

三、变种Builder模式
对于上述分析的两种方法，都存在优点和缺点，为了解决上述两种构建方式的不足，伟大的程序员们创造出了变种 Builder模式。直接看代码：
*/
/*
class Personz {
    //必要参数
    private final int id;
    private final String name;
    //可选参数
    private int age;
    private String sex;
    private String phone;
    private String address;
    private String desc;

    private Personz(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.phone = builder.phone;
        this.address = builder.address;
        this.desc = builder.desc;
    }

    public static class Builder {
        //必要参数
        private final int id;
        private final String name;
        //可选参数
        private int age;
        private String sex;
        private String phone;
        private String address;
        private String desc;
        
        public Personz build() {
           return new Personz(this);
       }

        public Builder(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public Builder age(int val) {
            this.age = val;
            return this;
        }

        public Builder sex(String val) {
            this.sex = val;
            return this;
        }

        public Builder phone(String val) {
            this.phone = val;
            return this;
        }

        public Builder address(String val) {
            this.address = val;
            return this;
        }

        public Builder desc(String val) {
            this.desc = val;
            return this;
        }

    }
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + ", age=" + age + ", sex="
				+ sex + ", phone=" + phone + ", address=" + address + ", desc=" + desc+ "]";
	}
}
*/
/*
观察上述代码，可以看到变种Builder模式包括以下内容：

（1）、在要构建类的内部，创建一个静态内部类Builder；

（2）、静态内部类的属性要与构建类的属性一致；

（3）、构建类的构造参数是静态内部类，使用静态内部类的变量为构建类逐一赋值；

（4）、静态内部类提供参数的setter方法，并且返回值是当前Builder对象；

（5）、最终提供一个build方法new出来一个构建类的对象，参数是当前Builder对象；

调用代码如下：
*/
/*
public class Java构造器 {

	public static void main(String[] args) {
		Personz person = new Personz.Builder(1, "张小毛").age(22).sex("男").desc("使用builder模式").build();
		Personz person1 = new Personz.Builder(1, "张小毛").build();
		System.out.println(person1.toString());//Person [id=1, name=张小毛, age=0, sex=null, phone=null, address=null, desc=null]
		System.out.println(person);//Person [id=1, name=张小毛, age=22, sex=男, phone=null, address=null, desc=使用builder模式]
	}
}
*/

//使用lombok之@builder和@Data,实现Builder模式
import lombok.Data;
import lombok.ToString;
import lombok.Builder;

@Data
@Builder
@ToString
class User6 {
    private Integer id;
    private String username;
    private String password;
}

public class Java构造器{
	public static void main(String[] args) {
    User6 user6 = User6.builder().id(1).username("user6").password("zxc123").build();
    System.out.println(user6); // User6(id=1, username=user6, password=zxc123)
	}
}

/*对变种Builer模式的总结：

变种Builder模式目的在于：减少对象创建过程中引入的多个构造函数、可选参数以及多个setter过度使用导致的不必要的复杂性。

优点就是：看起来很整齐；先赋值，后创建对象，最终调用build()方法才创建了构建类的对象，保证了状态的完整性。

缺点嘛，就是需要额外写的代码多了点。

四、小结：
变种Builer模式相比于重叠构造器模式和JavaBean模式，Builder模式实现的对象更利于使用。

对Builer模式使用方法的总结：

外部类的构造函数私有，且参数为静态内部类；
静态内部类拥有外部类相同的属；
为每一个属性，写一个方法，返回的是Builer；
最后一个方法是build方法，用于构建一个外部类；

*/

//【构建器的引入和使用】
//
//在尝试了使用JavaBeans模式解决出现问题之后，现在就需要一种构建对象的方式满足：
//
//达到构造器那样的数据安全性
//达到JavaBeans模式的可读性和灵活性
//答案自然是我们今天要结束的主角构建器，其实使用构建器很简单，关键是是否选择使用它；那么我们就来看看使用它的基本思想和步骤。
//
//-类中添加一个静态内部类，其中含有和要创建对象的类有一样的参数，还有一系列类似setter功能的方法；保证了灵活性
//-该静态内部类有一个转换本类参数至外部类参数的接口（配合外部类的私有构造器）；保证了安全性
//-客户端通过创建内部类来初始化要创建对象的成员变量,然后调用内部类接口获取外部类实例对象
//例子1：  

//public class Java构建器 {
//
//	public static void main(String[] args) {
//		// TODO 自动生成的方法存根
//		TelevisionWithBuilder twb=new TelevisionWithBuilder.Builder(100,200,300,"abc","def","1234").build();
//		System.out.println(twb);
//	}
//
//}
//
//class TelevisionWithBuilder {
//
//	//the required parameters
//	private int width;
//	private int height;
//	//the optional parameters
//	private float weight;
//	private String color;
//	private String brand;
//	private String serialNum;
//
//	//the constructor used for transfer the Builder's parameter to the Object that we want to create
//	private TelevisionWithBuilder(Builder builder) {
//		this.width = builder.width;
//		this.height = builder.height;
//		this.weight = builder.weight;
//		this.color = builder.color;
//		this.brand = builder.brand;
//		this.serialNum = builder.serialNum;
//	}
//
//	static class Builder {
//		private int width;
//		private int height;
//		private float weight = 0.0f;
//		private String color = "black";
//		private String brand = "panda";
//		private String serialNum = "ID1000";
//
//		// the default public constructor
//		public Builder(int width, int height) {
//			this.width = width;
//			this.height = height;
//		}
//
//		public Builder(int i, int j, int k, String string, String string2, String string3) {
//			// TODO 自动生成的构造函数存根
//		}
//
//		// following is the Builder's functional method
//		public Builder setWeight(float weight) {
//			this.weight = weight;
//			return this;
//		}
//
//		public Builder setColor(String color) {
//			this.color = color;
//			return this;
//		}
//
//		public Builder setBrand(String brand) {
//			this.brand = brand;
//			return this;
//		}
//
//		public Builder setSerialNum(String serialNum) {
//			this.serialNum = serialNum;
//			return this;
//		}
//
//		// use the private constructor to build the project
//		public TelevisionWithBuilder build() {
//			return new TelevisionWithBuilder(this);
//		}
//
//	}
//
//	@Override
//	public String toString() {
//		return "TelevisionWithBuilder [width=" + width + ", height=" + height + ", weight=" + weight + ", color="
//				+ color + ", brand=" + brand + ", serialNum=" + serialNum + "]";
//	}
//}

//在设计模式中对Builder模式的定义是用于构建复杂对象的一种模式，所构建的对象往往需要多步初始化或赋值才能完成。那么，在实际的开发过程中，我们哪些地方适合用
//到Builder模式呢？其中使用Builder模式来替代多参数构造函数是一个比较好的实践法则。
//
//考虑用一个类表示包装食品外面现实的营养成分，这些标签通常有许多的参数，如果用重叠构造器模式，就必须写多个构造器（constructor）:
//
//-不带参数的构造器
//-一个参数的构造器
//-二个参数的构造器
//…
//下面我们使用Java的构建器（Builder）模式
//例子1：
/*
class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    //NutritionFacts内部静态类Builder类
    //静态类和静态方法的使用比起来频率很低
    public static class Builder{
        //静态变量
        private  static int servingSize;
        private  static int servings ;

        private int calories=0;
        //Builder自己的构造方法
        public Builder(int servingSize,int servings){
            this.servingSize=servingSize;
            this.servings=servings;
        }
        //Builder的赋值方法;返回一个Builder对象;接下来可以接着操作
        public Builder calories( int val){
            this.calories=val;
            return this;
        }
        //Builder的赋值方法2;返回一个Builder对象;接下来可以接着操作
        //Builder的赋值方法3;返回一个Builder对象;接下来可以接着操作
        //Builder的赋值方法4;返回一个Builder对象;接下来可以接着操作
        //......

        //builder 方法返回的是NutritionFacts 类
        public NutritionFacts build(){
            return new NutritionFacts(this);
        }
    }
    //NutritionFacts的Constructor方法;传入一个builder对象;对自己的参数赋值
    private NutritionFacts(Builder builder){
        servingSize=builder.servingSize;
        servings=builder.servings;
        calories=builder.calories;
    }
    //NutritionFacts自己的操作方法
    public int getCalories(){
        return this.calories;
    }
    @Override
	public String toString() {
		return "NutritionFacts [servingSize=" + servingSize + ", servings=" + servings + ", calories=" + calories + "]";
	}
}
public class Java构建器 {
    public   static void main(String[] args){
    //通过new NutritionFacts.Builder(1,2)创建了一个Builder类
    //然后调用赋值方法calories给Builder的calories属性赋值;返回Builder类
    //可以继续调用赋值方法;我偷懒没写了TAT
    //最后调用Builder类的builde();返回我们需要的NutritionFacts对象。
     NutritionFacts nf=new NutritionFacts.Builder(1,2).calories(3).build();
        System.out.println(nf.getCalories());//3
        System.out.println(nf);//NutritionFacts [servingSize=1, servings=2, calories=3]
    }
}
*/